package com.sneaker.shower.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.sneaker.shower.common.BizException;
import com.sneaker.shower.common.ThreadLocalCache;
import com.sneaker.shower.entity.domain.menu.MenuDO;
import com.sneaker.shower.entity.domain.menu.MenuImageDO;
import com.sneaker.shower.entity.domain.menu.ProductDO;
import com.sneaker.shower.entity.dto.*;
import com.sneaker.shower.mapper.MenuMapper;
import com.sneaker.shower.mapstruct.MenuImageMapStruct;
import com.sneaker.shower.mapstruct.MenuMapStruct;
import com.sneaker.shower.mapstruct.ProductMapStruct;
import com.sneaker.shower.service.MenuImageService;
import com.sneaker.shower.service.MenuService;
import com.sneaker.shower.service.ProductService;
import com.sneaker.shower.util.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MenuServiceImpl extends ServiceImpl<MenuMapper, MenuDO> implements MenuService {

    private final ProductService productService;

    private final MenuImageService menuImageService;

    @Autowired
    public MenuServiceImpl(ProductService productService,
                           MenuImageService menuImageService) {
        this.productService = productService;
        this.menuImageService = menuImageService;
    }

    @Override
    public MenuDTO getMenu(String menuGuid) {
        String organizationGuid = ThreadLocalCache.get().getOrganizationGuid();
        MenuDO self = getOne(new LambdaQueryWrapper<MenuDO>().eq(MenuDO::getGuid, menuGuid).eq(MenuDO::getOrganizationGuid, Long.parseLong(organizationGuid)));
        if (ObjectUtils.isEmpty(self)) {
            log.info("未查询到菜单【{}】", menuGuid);
            return null;
        }
        MenuDTO menuDTO = MenuMapStruct.INSTANCE.do2DTO(self);
        menuDTO.setSubMenuList(getSubMenusAndProduct(menuGuid));
        menuDTO.setProductDTOList(getProducts(menuGuid));
        return menuDTO;
    }

    @Override
    public List<MenuDTO> getAll(Integer type) {
        String organizationGuid = ThreadLocalCache.get().getOrganizationGuid();
        LambdaQueryWrapper<MenuDO> aNull = new LambdaQueryWrapper<MenuDO>().eq(MenuDO::getOrganizationGuid, Long.parseLong(organizationGuid)).isNull(MenuDO::getParentGuid);
        if (type != null) {
            aNull.eq(MenuDO::getType, type);
        }
        List<MenuDO> list = list(aNull);
        if (CollectionUtils.isEmpty(list)) {
            throw new BizException("当前机构未配置菜单信息");
        }
        List<MenuDTO> menuDTOList = Lists.newArrayList();
        list.forEach(menu -> {
            MenuDTO menuDTO = getMenu(menu.getGuid() + "");
            menuDTOList.add(menuDTO);
        });
        return menuDTOList;
    }

    @Override
    public void create(MenuDTO menuDTO, Long orgGuid) {
        createMenu(menuDTO, null, orgGuid);
    }

    @Override
    public List<MenuImageDTO> getMenuDetails() {
        List<MenuImageDO> menuImageDOS = menuImageService.list(new LambdaQueryWrapper<MenuImageDO>()
                .eq(MenuImageDO::getOrganizationGuid, ThreadLocalCache.get().getOrganizationGuid())
                .notIn(MenuImageDO::getGuid, Arrays.asList(0L, 1L)).orderByAsc(MenuImageDO::getSort));
        log.info("orgGuid:{}", ThreadLocalCache.get().getOrganizationGuid());
        log.info(JacksonUtils.writeValueAsString(menuImageDOS));
        return menuImageDOS.stream().map(MenuImageMapStruct.INSTANCE::do2DTO).collect(Collectors.toList());
    }

    @Override
    public DetailDTO getDetail(Integer type, Long guid) {
        DetailDTO detailDTO = new DetailDTO();
        MenuImageDTO byGuidAndType = menuImageService.getByGuidAndType(type, guid);
        MenuDTO menu;
        if (guid == null) {
            List<MenuDTO> all = getAll(type);
            menu = all.get(0);
        } else {
            if (byGuidAndType.getIsMenu()) {
                String menuGuid = byGuidAndType.getMenuGuid();
                menu = getMenu(menuGuid);
                log.info("请求菜单详情返回数据：{}", JacksonUtils.writeValueAsString(detailDTO));
            } else {
                ProductDO byId = productService.getById(Long.parseLong(byGuidAndType.getMenuGuid()));
                MenuDO menuDOById = this.getById(byId.getMenuGuid());
                menu = MenuMapStruct.INSTANCE.do2DTO(menuDOById);
                ProductDTO productDTO = ProductMapStruct.INSTANCE.do2DTO(byId);
                menu.setProductDTOList(Collections.singletonList(productDTO));
            }
        }
        detailDTO.setMenuDTO(getParentMenuDTO(menu));
        detailDTO.setMenuImageDTO(byGuidAndType);
        return detailDTO;
    }

    @Override
    public List<MenuDTO> getFullMenuByProduct(SubOrderDTO subOrderDTO) {
        // 获取当前订单下的所有产品信息
        List<GoodsItemDTO> goodsItemDTOList = subOrderDTO.getGoodsItemDTOList();
        Set<String> productGuidSet = goodsItemDTOList.stream().map(GoodsItemDTO::getProductGuid).collect(Collectors.toSet());
        List<ProductDO> productDOS = productService.list(new LambdaQueryWrapper<ProductDO>().in(ProductDO::getGuid, productGuidSet));
        List<String> fullMenuGuidList = productDOS.stream().map(ProductDO::getFullMenuGuidList).collect(Collectors.toList());
        List<ProductDTO> productDTOS = ProductMapStruct.INSTANCE.doList2DTOList(productDOS);

        // 根据订单信息修改产品DTO 中的价格以及数量
        Map<String, GoodsItemDTO> goodsItemDTOMap = goodsItemDTOList.stream().collect(Collectors.toMap(GoodsItemDTO::getProductGuid, Function.identity()));
        for (ProductDTO productDTO : productDTOS) {
            GoodsItemDTO goodsItemDTO = goodsItemDTOMap.get(productDTO.getGuid());
            if (subOrderDTO.getIsChecked()) { // 如果订单是已经被客服确认了 就用商品里面的价格替换产品本来的价格
                productDTO.setPrice(goodsItemDTO.getPrice());
            }
            productDTO.setCount(goodsItemDTO.getCount());
        }

        Set<Long> menuGuidSet = new HashSet<>();
        fullMenuGuidList.forEach(s -> { // 找出所有菜单的guid
            String[] split = s.split(",");
            Set<Long> collect = Arrays.stream(split).map(Long::valueOf).collect(Collectors.toSet());
            if (!CollectionUtils.isEmpty(collect)) {
                menuGuidSet.addAll(collect);
            }
        });
        // 查询出所有菜单的信息
        List<MenuDO> allList = this.list(new LambdaQueryWrapper<MenuDO>().in(MenuDO::getGuid, menuGuidSet));
        // 根目录的信息
        List<MenuDO> firstMenuDOS = allList.stream().filter((e) -> e.getParentGuid() == null).collect(Collectors.toList());
        allList.removeAll(firstMenuDOS);

        // 所有子目录， 通过parentMenuGuid分组
        Map<Long, List<MenuDO>> subMenuMap = allList.stream().collect(Collectors.groupingBy(MenuDO::getParentGuid));
        List<MenuDTO> menuDTOList = MenuMapStruct.INSTANCE.doList2DTOList(firstMenuDOS);
        Map<String, List<ProductDTO>> productsMap = productDTOS.stream().collect(Collectors.groupingBy(ProductDTO::getMenuGuid));
        return getAllMenus(menuDTOList, subMenuMap, productsMap);
    }

    @Override
    public Boolean createImage(List<MenuImageDTO> menuImageDTOS) {
        int i = 0;
        List<MenuImageDO> menuImageDOS = Lists.newArrayList();
        for (MenuImageDTO menuImageDTO : menuImageDTOS) {
            MenuImageDO menuImageDO = MenuImageMapStruct.INSTANCE.dto2DO(menuImageDTO);
            menuImageDO.setGuid(IdWorker.getId());
            menuImageDO.setSort(i);
            i++;
            menuImageDOS.add(menuImageDO);
        }
        return menuImageService.saveBatch(menuImageDOS);
    }

    private List<MenuDTO> getAllMenus(List<MenuDTO> firstMenus, Map<Long, List<MenuDO>> subMenus, Map<String, List<ProductDTO>> productsMap) {
        if (firstMenus == null) {
            return Lists.newArrayList();
        }
        for (MenuDTO firstMenu : firstMenus) {
            List<MenuDO> menuDOS = subMenus.get(Long.parseLong(firstMenu.getGuid()));
            List<MenuDTO> menuDTOList = MenuMapStruct.INSTANCE.doList2DTOList(menuDOS);
            firstMenu.setProductDTOList(productsMap.get(firstMenu.getGuid()));
            firstMenu.setSubMenuList(getAllMenus(menuDTOList, subMenus, productsMap));
        }
        return firstMenus;
    }

    /**
     * 1
     * 递归获取所有子菜单及产品
     *
     * @param menuGuid menuGuid
     * @return List<MenuDTO>
     */
    private List<MenuDTO> getSubMenusAndProduct(String menuGuid) {
        List<MenuDO> list = list(new LambdaQueryWrapper<MenuDO>().eq(MenuDO::getParentGuid, menuGuid));
        List<MenuDTO> menuDTOS = MenuMapStruct.INSTANCE.doList2DTOList(list);
        for (MenuDTO menuDTO : menuDTOS) {
            menuDTO.setProductDTOList(getProducts(menuDTO.getGuid()));
            List<MenuDTO> subMenusAndProduct = getSubMenusAndProduct(menuDTO.getGuid());
            if (!CollectionUtils.isEmpty(subMenusAndProduct)) {
                menuDTO.setSubMenuList(subMenusAndProduct);
            }
        }
        return menuDTOS;
    }

    private List<ProductDTO> getProducts(String menuGuid) {
        List<ProductDO> productDOList = productService.list(new LambdaQueryWrapper<ProductDO>().eq(ProductDO::getMenuGuid, menuGuid));
        Map<Long, ProductDO> collect = productDOList.stream().collect(Collectors.toMap(ProductDO::getGuid, Function.identity()));
        List<ProductDTO> productDTOS = ProductMapStruct.INSTANCE.doList2DTOList(productDOList);
        productDTOS.forEach((e) ->
                e.setBeforeProductGuidList(
                        Arrays.asList(
                                Optional.ofNullable(collect.get(Long.parseLong(e.getGuid()))
                                        .getBeforeProductGuid()).orElse("").split(",")))
        );
        return productDTOS;
    }

    private void createMenu(MenuDTO menuDTO, String parentGuidS, Long orgGuid) {
        if (parentGuidS == null) {
            parentGuidS = "";
        }
        List<ProductDTO> productDTOList = menuDTO.getProductDTOList();
        List<MenuDTO> subMenuList = menuDTO.getSubMenuList();
        MenuDO menuDO = MenuMapStruct.INSTANCE.dto2DO(menuDTO);
        Long guid = IdWorker.getId();
        menuDO.setGuid(guid);
        menuDO.setOrganizationGuid(orgGuid);
        save(menuDO);
        parentGuidS = parentGuidS + (StringUtils.hasText(parentGuidS) ? "," : "") + guid;
        List<ProductDO> productDOS = ProductMapStruct.INSTANCE.dtoList2DO(productDTOList);
        if (!CollectionUtils.isEmpty(productDOS)) {
            for (ProductDO productDO : productDOS) {
                productDO.setGuid(IdWorker.getId());
                productDO.setMenuGuid(guid);
                productDO.setOrganizationGuid(orgGuid);
                productDO.setFullMenuGuidList(parentGuidS);
            }
            productService.saveBatch(productDOS);
        }
        if (!CollectionUtils.isEmpty(subMenuList)) {
            for (MenuDTO dto : subMenuList) {
                dto.setParentGuid(guid + "");
                createMenu(dto, parentGuidS, orgGuid);
            }
        }
    }

    private MenuDTO getParentMenuDTO(MenuDTO menuDTO) {
        if (StringUtils.isEmpty(menuDTO.getParentGuid())) {
            return menuDTO;
        }
        MenuDTO menuDTO1 = MenuMapStruct.INSTANCE.do2DTO(getById(Long.parseLong(menuDTO.getParentGuid())));
        menuDTO1.setSubMenuList(Collections.singletonList(menuDTO));
        if (menuDTO1.getParentGuid() != null) {
            return getParentMenuDTO(menuDTO1);
        }
        return menuDTO1;
    }
}

